﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace Project1
{
    public class LaserFire
    {
        #region Member Variables

        /// <summary>
        /// Our game
        /// </summary>
        private TurretGame mGame;

        /// <summary>
        /// The Laser blast model
        /// </summary>
        private Model mModel;

        private LinkedList<LaserBlast> mLaserBlasts = new LinkedList<LaserBlast>();

        #endregion

        #region Properties

        public LinkedList<LaserBlast> LaserBlasts { get { return mLaserBlasts; } }

        #endregion

        public class LaserBlast
        {
            public Vector3 mPosition;
            public Matrix mOrientation;
            public float mSpeed;
            public float mLife;
        }

        /// <summary>
        /// Our Constructor
        /// </summary>
        /// <param name="aGame"></param>
        public LaserFire(TurretGame aGame)
        {
            this.mGame = aGame;
        }

        /// <summary>
        /// This function is called to load content into this component
        /// of our game
        /// </summary>
        /// <param name="aContent"></param>
        public void LoadContent(ContentManager aContent)
        {
            mModel = aContent.Load<Model>("LaserBlast");
        }

        /// <summary>
        /// This function is called to update this component of our gam
        /// to the current game time.
        /// </summary>
        /// <param name="aGameTime"></param>
        public void Update(GameTime aGameTime)
        {
            float lDelta = (float)aGameTime.ElapsedGameTime.TotalSeconds;

            // Loop over all laser blasts in the list
            for (LinkedListNode<LaserBlast> lBlastNode = mLaserBlasts.First; lBlastNode != null; )
            {
                // lBlastNode is the current node and we set
                // lNextBlast to the next node in the list
                LinkedListNode<LaserBlast> lNextBlast = lBlastNode.Next;
                
                // This is the actual blast object we are working on
                LaserBlast lBlast = lBlastNode.Value;

                // Update the position
                Vector3 lDirection = Vector3.TransformNormal(new Vector3(0, 0, 1), lBlast.mOrientation);
                lBlast.mPosition += lDirection * lBlast.mSpeed * lDelta;

                // Decrease the life of the blast
                lBlast.mLife -= lDelta;
                if (lBlast.mLife <= 0)
                {
                    // When done, remove from the list
                    mLaserBlasts.Remove(lBlastNode);
                }

                lBlastNode = lNextBlast;
            }
        }

        /// <summary>
        /// This function is called to draw this game component.
        /// </summary>
        /// <param name="aGraphics"></param>
        /// <param name="aGameTime"></param>
        public void Draw(GraphicsDeviceManager aGraphics, GameTime aGameTime)
        {
            foreach (LaserBlast lBlast in mLaserBlasts)
            {
                DrawModel(aGraphics, mModel, lBlast.mOrientation * Matrix.CreateTranslation(lBlast.mPosition));
            }
        }

        /// <summary>
        /// Draws the laser blast models
        /// </summary>
        /// <param name="aGraphics"></param>
        /// <param name="aModel"></param>
        /// <param name="aWorld"></param>
        private void DrawModel(GraphicsDeviceManager aGraphics, Model aModel, Matrix aWorld)
        {
            Matrix[] lTransforms = new Matrix[aModel.Bones.Count];
            aModel.CopyAbsoluteBoneTransformsTo(lTransforms);

            foreach (ModelMesh lMesh in aModel.Meshes)
            {
                foreach (BasicEffect lEffect in lMesh.Effects)
                {
                    lEffect.EnableDefaultLighting();
                    lEffect.World = lTransforms[lMesh.ParentBone.Index] * aWorld;
                    lEffect.View = mGame.Camera.View;
                    lEffect.Projection = mGame.Camera.Projection;
                }
                lMesh.Draw();
            }
        }

        /// <summary>
        /// The function called to fire a laser from the model
        /// </summary>
        /// <param name="aPosition"></param>
        /// <param name="aOrientation"></param>
        /// <param name="aVelocity"></param>
        public void FireLaser(Vector3 aPosition, Matrix aOrientation, float aVelocity)
        {
            LaserBlast lBlast = new LaserBlast();
            lBlast.mPosition = aPosition;
            lBlast.mOrientation = aOrientation;
            lBlast.mSpeed = 3000.0f + aVelocity;        // cm/sec
            lBlast.mLife = 2.0f;             // 2.0 seconds

            mLaserBlasts.AddLast(lBlast);
        }

        /// <summary>
        /// Clears all of the laser blasts from the scene.
        /// </summary>
        public void ClearLaserBlasts()
        {
            // Once again I put this in a function because I don't want the
            // list to maintain it's privacy.
            mLaserBlasts.Clear();
        }

        public void Reset()
        {
            mLaserBlasts.Clear();
        }

    } //End class
}// End namespace
